package org.brainstretch.ejb.managers;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
import javax.persistence.*;
import javax.transaction.*;
import org.brainstretch.models.CompletionStatus;
import org.brainstretch.models.User;
import org.brainstretch.models.UserQuiz;
import org.brainstretch.models.actions.AnswerList;
import org.brainstretch.models.actions.AnswerStatus;
import org.brainstretch.models.actions.UserAnswer;
import org.brainstretch.models.document.Posting;
import org.brainstretch.models.document.Term;
import org.brainstretch.models.document.Tokenizer;
import org.brainstretch.models.quizzes.*;

/**
 *
 * @author mrodaitis
 */
@Stateless
@LocalBean
public class QuizManager implements PersistenceManager<Quiz> {
    
    @PersistenceUnit(unitName="BrainStretchPU")
    EntityManagerFactory entityManagerFactory;
    
    @Resource 
    UserTransaction utx;
    
    @Override
    public Quiz findById(long quizId){
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Quiz found = entityManager.find(Quiz.class, quizId);
        entityManager.close();
        return found;
    }
    
    public QuestionSet findQuestionSetById(long id){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        QuestionSet found = entityManager.find(QuestionSet.class, id);
        entityManager.close();
        return found;
    }
    

    
    private void persistEntity(Object entity){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.persist(entity);
        entityManager.close();
    }
    
    public void persistClue(Clue clue){
        persistEntity(clue);
    }
    
    @Override
    public void persist(Quiz quiz){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.persist(quiz);
        entityManager.close();
    }
    
    public Quiz create(User creator){   
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Quiz quiz = new Quiz();
        quiz.setTitle(creator.getName()+"'s New Quiz");
        quiz.setCreator(creator);        
        entityManager.persist(quiz);
        entityManager.close();
        return quiz;
    }
    
    @Override
    public Quiz update(Quiz quiz){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Quiz managedQuiz = entityManager.merge(quiz);         
        
        Query getExistingPostings = entityManager.createQuery("SELECT posting FROM Posting posting JOIN posting.quiz quiz WHERE quiz.id = ?1");
        List<Posting> postings = getExistingPostings.setParameter(1, quiz.getId()).getResultList();
        for(Posting posting: postings){
            posting.getTerm().getPostings().remove(posting);
            entityManager.remove(posting);
        }
              
        int numQuizzes = ((Long)entityManager.createQuery("SELECT COUNT(quiz) FROM Quiz quiz").getSingleResult()).intValue();
        Map<String, Posting> newPostings = Tokenizer.getAllPostings(quiz);
        
        
        Query getTerms = entityManager.createQuery("SELECT term FROM Term term WHERE term.text IN :terms");
        List<String> newTerms = new ArrayList<String>();
        for(String term: newPostings.keySet()){
            newTerms.add(term);
        }
        List<Term> terms = getTerms.setParameter("terms", newTerms).getResultList();
        for(String termString: newTerms){
            Term term = findTerm(termString, terms);
            Posting posting = newPostings.get(termString.toLowerCase());
            boolean newTerm = term == null;
            if(newTerm){
                term = new Term();
                term.setText(termString);
            }
            term.getPostings().add(posting);
            posting.setTerm(term);
            term.calculateDF(numQuizzes);
            
            if(newTerm){
                entityManager.persist(term);
            }
            else{
                entityManager.merge(term);
            }
        }
        List<Term> allTerms = entityManager.createQuery("SELECT term FROM Term term").getResultList();
        updateAllTerms(allTerms, numQuizzes, entityManager);
        
        managedQuiz.calculateQuizMetrics();
        
        entityManager.close();
        return managedQuiz;
    }
    
    private void updateAllTerms(List<Term> terms, int numQuizzes, EntityManager em){
        em.joinTransaction();
        for(Term term: terms){
            term.calculateDF(numQuizzes);            
            System.err.println(term.getText()+" df:"+term.getDocumentFreq()+" idf:"+term.getInverseDocumentFreq());
            if(term.getDocumentFreq() == 0.0d || Double.isInfinite(term.getInverseDocumentFreq())){
                term.setInverseDocumentFreq(0);
                em.remove(term);
            }
            else{
                em.merge(term);
            }
        }
    }
    
    private Term findTerm(String term, List<Term> terms){
        Term found = null;
        for(int i = 0; i<terms.size() && found == null; i++){
            Term current = terms.get(i);
            if(current.getText().equals(term)){
                found = current;
            }
        }
        return found;
    }
    
    @Override
    public void delete(Quiz quiz){        
       EntityManager entityManager = entityManagerFactory.createEntityManager();
       quiz = entityManager.find(Quiz.class, quiz.getId());
       if(quiz != null){
           entityManager.remove(quiz);
       }
       entityManager.close();
    }    
    @Override
    public List<Quiz> list(){        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        List<Quiz> quizzes = entityManager.createQuery("SELECT quiz FROM Quiz quiz").getResultList();
        entityManager.close();
        return quizzes;
    }
    
    
    public List<QuestionSet> getQuestionSets(Quiz quiz){          
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        List<QuestionSet> results = entityManager.createQuery("SELECT questionSet FROM Quiz quiz JOIN quiz.questionSets questionSet WHERE quiz.id = ?1")
                            .setParameter(1, quiz.getId()).getResultList();
        entityManager.close();
        return results;
        
    }
    
    public List<Quiz> mostRecent(int count){               
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query query = entityManager.createQuery("SELECT quiz FROM Quiz quiz ORDER BY quiz.creationDate DESC");
        query.setMaxResults(count);    
        List<Quiz> results  = query.getResultList();
        entityManager.close();
        return results;
    }
    
    public QuizInstance getQuizInstanceFor(Quiz quiz, User user){
        QuizInstance instance = new QuizInstance(quiz);
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
//        entityManager.getTransaction().begin();
        try {
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
            instance.setAuthToken(EJBUtils.hashString(""+random.nextLong()));
        } catch (Exception ex) {
            //TODO DEBUG
            System.out.println("SecureRandom generation failed");
            instance.setAuthToken("" + new Date().getTime());
        }
        //entityManager.persist(instance);
//        entityManager.getTransaction().commit();
        
//        entityManager.getTransaction().begin();
        UserQuiz quizAction = new UserQuiz(user, instance, quiz); 
        quizAction.setPointsPossible(instance.getQuestions().size());
        
        
        if(user != null){
            user = entityManager.merge(user);
            user.getQuizzesTaken().add(quizAction);
            entityManager.merge(user);
//            entityManager.merge(user);
        }
        else{
            entityManager.persist(quizAction);
        }
//        entityManager.getTransaction().commit();
        entityManager.close();
        return instance;
    }
    
    public AnswerList getAnswersForUnansweredQuestions(UserQuiz quizAction){        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        List<Question> questions = quizAction.getInstance().getQuestions();
        for(AnswerList answerList: quizAction.getAnswers()){
            for(UserAnswer ans: answerList.getAnswers()){
                if(ans.getStatus() == AnswerStatus.Correct){
                    questions.remove(ans.getQuestion());
                }
            }
        }
        
        AnswerList correct = new AnswerList();
        for(Question q: questions){
            UserAnswer ans = new UserAnswer(q, q.getSolution().getText());
            ans.setStatus(AnswerStatus.Correct);
            correct.getAnswers().add(ans);
        }
        entityManager.close();
        return correct;
    }
    
    public void finishQuiz(UserQuiz quizAction, CompletionStatus status){        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        quizAction.setStatus(status);
        quizAction.setCompletionTime(new Date());
        entityManager.merge(quizAction);
        Quiz quiz = quizAction.getQuiz();
        Query findAllActions = entityManager.createQuery(
            "SELECT uq FROM UserQuiz uq JOIN uq.quiz quiz JOIN quiz.profile profile  WHERE quiz.id = ?1 AND uq.completionTime is not null AND uq.completionTime > profile.lastUpdated"
                );
        List<UserQuiz> actions = findAllActions.setParameter(1, quiz.getId()).getResultList();
        quiz.calculateUserMetrics(actions);
        entityManager.merge(quiz);
        entityManager.close();
    }
    
    public void score(AnswerList answers, UserQuiz quizAction){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query findQuestion = entityManager.createQuery("SELECT question FROM Question question WHERE question.id = ?1");
        quizAction = entityManager.merge(quizAction);
        
        for(UserAnswer answer: answers.getAnswers()){
            try{
                Question question = (Question)findQuestion.setParameter(1, answer.getQuestion().getId()).getSingleResult();
                gradeAnswer(answer, question);
            }catch(Exception ex){
                //TODO log this
                ex.printStackTrace();
            }                      
        }
        
        if(quizAction != null){
            answers.setSubmissionTime(new Date());
            entityManager.persist(answers);
            quizAction.getAnswers().add(answers);
            quizAction.setPoints(countPoints(quizAction));
//            entityManager.merge(quizAction);
        }
        entityManager.close();
    }
    
    void gradeAnswer(UserAnswer answer, Question question){
            boolean correct = question.getSolution().isCorrect(answer.getText());
            answer.setStatus(correct? AnswerStatus.Correct: AnswerStatus.Incorrect);
    }
    
    void removeInvalidQuestions(AnswerList answers, QuizInstance instance){
        for(int i = answers.getAnswers().size()-1; i>=0; i--){
            UserAnswer answer = answers.getAnswers().get(i);
            if(!instance.getQuestions().contains(answer.getQuestion())){
                answers.getAnswers().remove(i);
            }
        }
    }
    
    int countPoints(UserQuiz action){
            List<Question> solved = new ArrayList<Question>();
            for(AnswerList list: action.getAnswers()){
                for(UserAnswer ans: list.getAnswers()){
                    if(ans.getStatus() == AnswerStatus.Correct && !solved.contains(ans.getQuestion())){
                        solved.add(ans.getQuestion());
                    }
                }
            }
            return solved.size();
    }
    
    public List<UserQuiz> getQuizzesForUser(User user){
        if(user == null){
            throw new IllegalArgumentException("User cannot be null");
        }
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query findQuizzes = entityManager.createQuery("SELECT user.quizzesTaken FROM User user WHERE user.id = ?1");
        List<UserQuiz> results = findQuizzes.setParameter(1, user.getId()).getResultList();
        entityManager.close();
        return results;
    }
    
    public List<UserQuiz> getQuizzesForUser(User user, Category category){
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query findQuizzes = entityManager.createQuery("SELECT uQuiz FROM User user JOIN user.quizzesTaken uQuiz JOIN uQuiz.quiz quiz WHERE user.id = ?1 AND quiz.category =?2");
        List<UserQuiz> results = findQuizzes.setParameter(1, user.getId()).setParameter(2, category).getResultList(); 
        entityManager.close();
        return results;
    }
    
    public UserQuiz findActionFor(User user, String authtoken, Long quizId){
        
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        UserQuiz action = null;
        Query findAction = entityManager.createQuery("SELECT qa FROM UserQuiz qa WHERE qa.instance.authToken = ?1 AND qa.quiz.id = ?2");
        try{
            action = (UserQuiz)findAction.setParameter(1, authtoken).setParameter(2, quizId).getSingleResult();
        }catch(NoResultException ex){
            ex.printStackTrace();
        }
        entityManager.close();
        return action;
    }
    
    
}
